#ifndef __SHAPE_APPROX__
#define __SHAPE_APPROX__

#include <vector>
#include <memory>

struct MatrixElement {
	MatrixElement(int row = 0, int col = 0)
	{
		this->row = row;
		this->col = col;
	}

	int row, col;
};

bool operator==(const MatrixElement &m1, const MatrixElement &m2);

class Matrix {
public:
	Matrix()
	{
		data = 0;
		set_data(0, 0, 0);
	};

	Matrix(int width, int height)
	{
		data = 0;
		set_data(width, height, 0);
	};

	Matrix(const Matrix &m)
	{
		data = 0;
		set_data(m.width, m.height, m.data);
	}

	Matrix& operator=(const Matrix &m)
	{
		set_data(m.width, m.height, m.data);

		return *this;
	}

	~Matrix()
	{
		delete []data;
	};

	inline int at(int row, int col) const
	{
		return data[col + row * width];
	}

	inline int at(const MatrixElement &m) const
	{
		return at(m.row, m.col);
	}

	inline int &at(int row, int col)
	{
		return data[col + row * width];
	}

	inline int &at(const MatrixElement &m)
	{
		return at(m.row, m.col);
	}

	inline bool IsInner(const MatrixElement &m) const
	{
		return IsInner(m.row, m.col);
	}

	inline bool IsInner(int row, int col) const
	{
		return row >= 0 && row < height
			&& col >= 0 && col < width;
	}

	inline int Width() const
	{
		return width;
	}

	inline int Height() const
	{
		return height;
	}

private:
	void set_data(int width, int height, int *data)
	{
		this->width = width;
		this->height = height;

		if (this->data != 0)
		{
			delete []this->data;
		}

		this->data = new int[width * height];

		if (data != 0)
		{
			std::memcpy(this->data, data, width * height * sizeof(int));
		}
		else
		{
			std::memset(this->data, 0, width * height * sizeof(int));
		}
	}

	int width, height;
	int *data;
};

bool operator==(const Matrix &m1, const Matrix &m2);

struct Point {
	Point(double x = 0, double y = 0)
	{
		this->x = x;
		this->y = y;
	}

	double x, y;
};

bool operator==(const Point &p1, const Point &p2);

struct Shape {
	std::vector<Point> points;
};

bool operator==(const Shape &s1, const Shape &s2);

inline std::vector<MatrixElement> get_neighbour_points(const Matrix &m, MatrixElement p);
void fill_background(Matrix &inner_input, MatrixElement startFrom);
std::vector<std::pair<int, MatrixElement> > segment_shapes(Matrix &inner_input);
void delate(Matrix &input, std::vector<std::pair<int, MatrixElement> > &shapes);
std::vector<Shape> find_basic_shapes(Matrix &input, std::vector<std::pair<int, MatrixElement> > &shapes_points);
std::vector<Shape> remove_trivial_vertexes(const std::vector<Shape> &shapes);

std::vector<Shape> find_shapes(const Matrix &input);
std::vector<Shape> approximate(std::vector<Shape> shapes, double precision);

#endif